home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
magazine
/
msysjour
/
vol07
/
02
/
controls
/
muscroll.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-02-29
|
14KB
|
449 lines
/*
* MUSCROLL.C
*
* Contains the main window procedure of the MicroScroll control
* that handles mouse logic, and Windows messages.
*
* Version 1.1, October 1991, Kraig Brockschmidt
*/
#include <windows.h>
#include "muscrdll.h"
/*
* MicroScrollWndProc
*
* Purpose:
* Window Procedure for the MicroScroll custom control. Handles all
* messages like WM_PAINT just as a normal application window would.
* Any message not processed here should go to DefWindowProc.
*
* Parameters:
* hWnd HWND handle to the control window.
* iMsg WORD message identifier.
* wParam WORD parameter of the message.
* lParam LONG parameter of the message.
*
* Return Value:
* LONG Varies with the message.
*
*/
LONG FAR PASCAL MicroScrollWndProc(HWND hWnd, WORD iMsg,
WORD wParam, LONG lParam)
{
PMUSCROLL pMS;
POINT pt;
RECT rect;
short x, y;
short cx, cy;
WORD wState;
/*
* Get a pointer to the MUSCROLL structure for this control.
* Note that if we do this before WM_NCCREATE where we allocate
* the memory, pMS will be NULL, which is not a problem since
* we do not access it until after WM_NCCREATE.
*/
pMS=(PMUSCROLL)GetWindowWord(hWnd, GWW_MUSCROLLHMEM);
//Let the API handler process WM_USER+xxxx messages
if (iMsg >= WM_USER)
return LMicroScrollAPI(hWnd, iMsg, wParam, lParam, pMS);
//Handle standard Windows messages.
switch (iMsg)
{
case WM_NCCREATE:
case WM_CREATE:
return LMicroScrollCreate(hWnd, iMsg, pMS, (LPCREATESTRUCT)lParam);
case WM_NCDESTROY:
//Free the control's memory.
LocalFree((HANDLE)pMS);
break;
case WM_ERASEBKGND:
/*
* Eat this message to avoid erasing portions that
* we are going to repaint in WM_PAINT. Part of a
* change-state-and-repaint strategy is to rely on
* WM_PAINT to do anything visual, which includes
* erasing invalid portions. Letting WM_ERASEBKGND
* erase the background is redundant.
*/
break;
case WM_PAINT:
return LMicroScrollPaint(hWnd, pMS);
case WM_ENABLE:
/*
* Handles disabling/enabling case. Example of a
* change-state-and-repaint strategy since we let the
* painting code take care of the visuals.
*/
if (wParam)
StateClear(pMS, MUSTATE_GRAYED);
else
StateSet(pMS, MUSTATE_GRAYED);
//Force a repaint since the control will look different.
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
break;
case WM_SHOWWINDOW:
/*
* Set or clear the hidden flag. Windows will
* automatically force a repaint if we become visible.
*/
if (wParam)
StateClear(pMS, MUSTATE_HIDDEN);
else
StateSet(pMS, MUSTATE_HIDDEN);
break;
case WM_CANCELMODE:
/*
* IMPORTANT MESSAGE! WM_CANCELMODE means that a
* dialog or some other modal process has started.
* we must make sure that we cancel any clicked state
* we are in, kill the timers, and release the capture.
*/
StateClear(pMS, MUSTATE_DOWNCLICK | MUSTATE_UPCLICK);
KillTimer(hWnd, IDT_FIRSTCLICK);
KillTimer(hWnd, IDT_HOLDCLICK);
ReleaseCapture();
break;
case WM_TIMER:
/*
* We run two timers: the first is the initial delay
* after the first click before we begin repeating, the
* second is the repeat rate.
*/
if (wParam==IDT_FIRSTCLICK)
{
KillTimer(hWnd, wParam);
SetTimer(hWnd, IDT_HOLDCLICK, CTICKS_HOLDCLICK, NULL);
}
/*
* Send a new scroll message if the mouse is still in the
* originally clicked area.
*/
if (!StateTest(pMS, MUSTATE_MOUSEOUT))
PositionChange(hWnd, pMS);
break;
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
/*
* When we get a mouse down message, we know that the mouse
* is over the control. We then do the following steps
* to set up the new state:
* 1. Hit-test the coordinates of the click to
* determine in which half the click occurred.
* 2. Set the appropriate MUSTATE_*CLICK state
* and repaint that clicked half. This is another
* example of a change-state-and-repaint strategy.
* 3. Send an initial scroll message.
* 4. Set the mouse capture.
* 5. Set the initial delay timer before repeating
* the scroll message.
*
* A WM_LBUTTONDBLCLK message means that the user clicked
* the control twice in succession which we want to treat
* like WM_LBUTTONDOWN. This is safe since we will receive
* WM_LBUTTONUP before the WM_LBUTTONDBLCLK.
*/
//Get the mouse coordinates.
x=LOWORD(lParam);
y=HIWORD(lParam);
/*
* Only need to hit-test the upper half for a vertical
* control or the left half for a horizontal control.
*/
GetClientRect(hWnd, &rect);
cx=rect.right >> 1;
cy=rect.bottom >> 1;
if (MSS_VERTICAL & pMS->dwStyle)
wState=(y > cy) ? MUSTATE_DOWNCLICK : MUSTATE_UPCLICK;
else
wState=(x > cx) ? MUSTATE_RIGHTCLICK : MUSTATE_LEFTCLICK;
//Change-state-and-repaint
StateSet(pMS, wState);
ClickedRectCalc(hWnd, pMS, &rect);
InvalidateRect(hWnd, &rect, TRUE);
UpdateWindow(hWnd);
PositionChange(hWnd, pMS);
SetCapture(hWnd);
SetTimer(hWnd, IDT_FIRSTCLICK, CTICKS_FIRSTCLICK, NULL);
break;
case WM_MOUSEMOVE:
/*
* On WM_MOUSEMOVE messages we want to know if the mouse
* has moved out of the control when the control is in
* a clicked state. If the control has not been clicked,
* then we have nothing to do. Otherwise we want to set
* the MUSTATE_MOUSEOUT flag and repaint so the button
* visually comes up.
*/
if (!StateTest(pMS, MUSTATE_CLICKED))
break;
//Get the area we originally clicked and the new POINT
ClickedRectCalc(hWnd, pMS, &rect);
pt=MAKEPOINT(lParam);
wState=pMS->wState;
//Hit-Test the rectange and change the state if necessary.
if (PtInRect(&rect, pt))
StateClear(pMS, MUSTATE_MOUSEOUT);
else
StateSet(pMS, MUSTATE_MOUSEOUT);
/*
* If the state changed, repaint the appropriate part of
* the control.
*/
if (wState!=pMS->wState)
{
InvalidateRect(hWnd, &rect, TRUE);
UpdateWindow(hWnd);
}
break;
case WM_LBUTTONUP:
/*
* A mouse button up event is much like WM_CANCELMODE since
* we have to clean out whatever state the control is in:
* 1. Kill any repeat timers we might have created.
* 2. Release the mouse capture.
* 3. Clear the clicked states and repaint, another example
* of a change-state-and-repaint strategy.
*/
KillTimer(hWnd, IDT_FIRSTCLICK);
KillTimer(hWnd, IDT_HOLDCLICK);
ReleaseCapture();
/*
* Repaint if necessary, only if we are clicked AND the mouse
* is still in the boundaries of the control.
*/
if (StateTest(pMS, MUSTATE_CLICKED) &&
StateTest(pMS, ~MUSTATE_MOUSEOUT))
{
//Calculate the rectangle before clearing states.
ClickedRectCalc(hWnd, pMS, &rect);
//Clear the states so we repaint properly.
StateClear(pMS, MUSTATE_MOUSEOUT);
StateClear(pMS, MUSTATE_CLICKED);
InvalidateRect(hWnd, &rect, TRUE);
UpdateWindow(hWnd);
}
//Insure that we clear out the states.
break;
default:
return DefWindowProc(hWnd, iMsg, wParam, lParam);
}
return 0L;
}
/*
* ClickedRectCalc
*
* Purpose:
* Calculates the rectangle of the clicked region based on the
* state flags MUSTATE_UPCLICK, MUSTATE_DOWNCLICK, MUSTATE_LEFTCLICK,
* and MUSTATE_RIGHTLICK, depending on the style.
*
* Parameter:
* hWnd HWND handle to the control window.
* lpRect LPRECT rectangle structure to fill.
*
* Return Value:
* void
*
*/
void PASCAL ClickedRectCalc(HWND hWnd, PMUSCROLL pMS, LPRECT lpRect)
{
short cx, cy;
GetClientRect(hWnd, lpRect);
cx=lpRect->right >> 1;
cy=lpRect->bottom >> 1;
if (MSS_VERTICAL & pMS->dwStyle)
{
if (StateTest(pMS, MUSTATE_DOWNCLICK))
lpRect->top=cy;
if (StateTest(pMS, MUSTATE_UPCLICK))
lpRect->bottom=1+cy;
}
else
{
//MSS_HORIZONTAL
if (StateTest(pMS, MUSTATE_RIGHTCLICK))
lpRect->left=cx;
if (StateTest(pMS, MUSTATE_LEFTCLICK))
lpRect->right=1+cx;
}
return;
}
/*
* PositionChange
*
* Purpose:
* Checks what part of the control is clicked, modifies the current
* position accordingly (taking MSS_INVERTRANGE into account) and
* sends an appropriate message to the associate. For MSS_VERTICAL
* controls we send WM_VSCROLL messages and for MSS_HORIZONTAL controls
* we send WM_HSCROLL.
*
* The scroll code in the message is always SB_LINEUP for the upper
* or left half of the control (vertical and horizontal, respectively)
* and SB_LINEDOWN for the bottom or right half.
*
* This function does not send a message if the position is pegged
* on the minimum or maximum of the range if MSS_NOPEGSCROLL is
* set in the style bits.
*
* Parameters:
* hWnd HWND of the control.
* pMS PMUSCROLL pointer to control data structure.
*
* Return Value:
* void
*/
void PASCAL PositionChange(HWND hWnd, PMUSCROLL pMS)
{
WORD wScrollMsg;
WORD wScrollCode;
BOOL fPegged=FALSE;
if (StateTest(pMS, MUSTATE_UPCLICK | MUSTATE_LEFTCLICK))
wScrollCode=SB_LINEUP;
if (StateTest(pMS, MUSTATE_DOWNCLICK | MUSTATE_RIGHTCLICK))
wScrollCode=SB_LINEDOWN;
wScrollMsg=(MSS_VERTICAL & pMS->dwStyle) ? WM_VSCROLL : WM_HSCROLL;
/*
* Modify the current position according to the following rules:
*
* 1. On SB_LINEUP with an inverted range, increment the position.
* If the position is already at the maximum, set the pegged flag.
*
* 2. On SB_LINEUP with an normal range, decrement the position.
* If the position is already at the minimum, set the pegged flag.
*
* 3. On SB_LINEDOWN with an inverted range, treat like SB_LINEUP
* with a normal range.
*
* 4. On SB_LINEDOWN with an normal range, treat like SB_LINEUP
* with an inverted range.
*/
if (wScrollCode==SB_LINEUP)
{
if (MSS_INVERTRANGE & pMS->dwStyle)
{
if (pMS->iPos==pMS->iMax)
fPegged=TRUE;
else
pMS->iPos++;
}
else
{
if (pMS->iPos==pMS->iMin)
fPegged=TRUE;
else
pMS->iPos--;
}
}
else
{
if (MSS_INVERTRANGE & pMS->dwStyle)
{
if (pMS->iPos==pMS->iMin)
fPegged=TRUE;
else
pMS->iPos--;
}
else
{
if (pMS->iPos==pMS->iMax)
fPegged=TRUE;
else
pMS->iPos++;
}
}
/*
* Send a message if we changed and are not pegged, or did not change
* and MSS_NOPEGSCROLL is clear.
*/
if (!fPegged || !(MSS_NOPEGSCROLL & pMS->dwStyle))
{
SendMessage(pMS->hWndAssociate, wScrollMsg,
wScrollCode, MAKELONG(pMS->iPos, hWnd));
}
return;
}